<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        // 硬绑定可以把this强制绑定到指定的对象(除了使用new时)
        // 防止函数调用应用默认绑定规则 问题在于 
        // 硬绑定会大大降低函数的灵活性 
        // 使用硬绑定之后就无法使用隐式绑定或显式绑定修改this(优先级)
        // 如果可以给默认绑定指定一个全局对象和undefined以外的值
        // 就可以实现和硬绑定相同的效果 同时保留用显式或隐式绑定修改this的能力
        if (!Function.prototype.softBind) {
            //在原型的基础上通过prototype新增属性或方法
            //每一个function，都会具备一个softBind方法
            Function.prototype.softBind = function (obj) {
                var fn = this;
                // 捕获所有curried参数
                // arguments是一个对象而不是数组  而且自身的原型链上也没有slice这个方法
                // []自身也是也是一个对象  而数组原型链上有这个slice这个方法
                // 通过call显式绑定来实现arguments变相有slice这个方法
                var curried = [].slice.call(arguments, 1);
                var bound = function () {
                    return fn.apply(
                        (!this || this === (window || global)) ?
                            obj : this,
                        // 这是为了合并两次传入的参数  才能做到柯里化
                        curried.concat.apply(curried, arguments)
                    );
                }
                bound.prototype = Object.create(fn.prototype);
                return bound;
            }
        }
        // 除了软绑定之外 softBind(...)的其他原理和ES5内置的bind(...)类似
        // 它会对指定的函数进行封装 首先检查调用时的this 
        // 如果this绑定到全局对象或者undefined 那就把指定的默认对象obj绑定到this
        // 否则不会修改this 此外这一段代码还支持柯里化

        function foo() {
            console.log("name: " + this.name);
        }

        var obj = { name: "obj" },
            obj2 = { name: "obj2" },
            obj3 = { name: "obj3" };

        var fooOBJ = foo.softBind(obj);
        fooOBJ();

        // 引用赋值
        obj2.foo = foo.softBind(obj);
        obj2.foo();

        // 也可以正常使用软绑定
        fooOBJ.call(obj3);

        // 在应用默认规则时 也会重新绑定到默认的obj对象上
        setTimeout(obj2.foo, 10);
    </script>

</body>

</html>